package com.ruoyi.bigdata;

import com.ruoyi.system.domain.SysOperLog;
import com.ruoyi.system.mapper.SysOperLogMapper;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

@Service
public class HighPerformanceExcelExportService {

    @Resource
    private SysOperLogMapper sysOperLogMapper;

    /**
     * 高性能导出SysOperLog数据到Excel文件
     *
     * @param start 起始记录数(从1开始)
     * @param end 结束记录数
     * @param outputStream 输出流
     */
    public void exportSysOperLogToExcel(int start, int end, OutputStream outputStream) throws IOException {
        int totalRecords = end - start + 1;
        int pageSize = 10000; // 每页查询10000条记录
        int totalPages = (totalRecords + pageSize - 1) / pageSize;

        // 创建工作簿和工作表
        Workbook workbook = new SXSSFWorkbook(1000); // 使用SXSSF支持大数据量
        Sheet sheet = workbook.createSheet("SysOperLog");

        // 创建表头
        createHeader(sheet);

        // 创建线程池
        int threadPoolSize = Runtime.getRuntime().availableProcessors() + 2;
        ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);

        // 使用线程安全的队列存储查询结果
        BlockingQueue<PageData> dataQueue = new LinkedBlockingQueue<>();

        // 使用CountDownLatch等待所有查询任务完成
        CountDownLatch queryLatch = new CountDownLatch(totalPages);

        // 提交分页查询任务
        for (int i = 0; i < totalPages; i++) {
            final int pageIndex = i;
            executor.submit(() -> {
                try {
                    int offset = (pageIndex * pageSize) + start - 1;
                    int limit = Math.min(pageSize, end - offset);

                    // 执行分页查询
                    List<SysOperLog> pageData = sysOperLogMapper.selectPage(offset, limit);
                    dataQueue.offer(new PageData(pageIndex, pageData));
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    queryLatch.countDown();
                }
            });
        }

        // 启动写入线程，按顺序将数据写入Excel
        CountDownLatch writeLatch = new CountDownLatch(1);
        AtomicInteger completedTasks = new AtomicInteger(0);
        Map<Integer, List<SysOperLog>> orderedData = new ConcurrentHashMap<>();

        executor.submit(() -> {
            try {
                int expectedPageIndex = 0;
                while (completedTasks.get() < totalPages) {
                    PageData pageData = dataQueue.poll(100, TimeUnit.MILLISECONDS);
                    if (pageData != null) {
                        orderedData.put(pageData.pageIndex, pageData.data);

                        // 按顺序写入数据
                        List<SysOperLog> currentData;
                        while ((currentData = orderedData.remove(expectedPageIndex)) != null) {
                            writeToSheet(workbook, currentData, expectedPageIndex * pageSize + 1);
                            expectedPageIndex++;
                            completedTasks.incrementAndGet();
                        }
                    }
                }

                // 处理剩余数据
                for (int i = expectedPageIndex; i < totalPages; i++) {
                    List<SysOperLog> remainingData = orderedData.remove(i);
                    if (remainingData != null) {
                        writeToSheet(workbook, remainingData, i * pageSize + 1);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                writeLatch.countDown();
            }
        });

        try {
            // 等待所有查询任务完成
            queryLatch.await();
            // 等待写入任务完成
            writeLatch.await();

            // 写入文件
            workbook.write(outputStream);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        } finally {
            executor.shutdown();
            workbook.close();
        }
    }

    /**
     * 创建Excel表头
     */
    private void createHeader(Sheet sheet) {
        Row headerRow = sheet.createRow(0);
        String[] headers = {
                "标题", "业务类型", "方法名称", "请求方法", "操作类别",
                "操作人员", "部门名称", "请求URL", "IP地址", "操作地点",
                "请求参数", "返回结果", "状态", "错误消息", "操作时间", "消耗时间"
        };

        for (int i = 0; i < headers.length; i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers[i]);
        }
    }

    /**
     * 将数据写入工作表
     */
    /**
     * 将数据写入工作表
     */
    /**
     * 将数据写入工作表（支持多sheet）
     */
    private void writeToSheet(Workbook workbook, List<SysOperLog> data, int startRow) {
        final int MAX_ROWS_PER_SHEET = 1048575;
        final int MAX_DATA_ROWS_PER_SHEET = MAX_ROWS_PER_SHEET; // 除去表头后的数据行数

        for (int i = 0; i < data.size(); i++) {
            int actualRowNum = startRow + i;
            int sheetIndex = actualRowNum / MAX_DATA_ROWS_PER_SHEET;
            int rowInSheet = actualRowNum % MAX_DATA_ROWS_PER_SHEET + 1; // +1是因为第0行是表头

            // 获取或创建工作表
            Sheet sheet;
            if (workbook.getNumberOfSheets() <= sheetIndex) {
                sheet = workbook.createSheet("SysOperLog_" + (sheetIndex + 1));
                createHeader(sheet); // 为新工作表创建表头
            } else {
                sheet = workbook.getSheetAt(sheetIndex);
            }

            SysOperLog log = data.get(i);
            Row row = sheet.createRow(rowInSheet);

            row.createCell(0).setCellValue(log.getTitle());
            row.createCell(1).setCellValue(log.getBusinessType());
            row.createCell(2).setCellValue(log.getMethod());
            row.createCell(3).setCellValue(log.getRequestMethod());
            row.createCell(4).setCellValue(log.getOperatorType());
            row.createCell(5).setCellValue(log.getOperName());
            row.createCell(6).setCellValue(log.getDeptName());
            row.createCell(7).setCellValue(log.getOperUrl());
            row.createCell(8).setCellValue(log.getOperIp());
            row.createCell(9).setCellValue(log.getOperLocation());
            row.createCell(10).setCellValue(log.getOperParam());
            row.createCell(11).setCellValue(log.getJsonResult());
            row.createCell(12).setCellValue(log.getStatus());
            row.createCell(13).setCellValue(log.getErrorMsg());
            row.createCell(14).setCellValue(log.getOperTime() != null ? log.getOperTime().toString() : "");
            row.createCell(15).setCellValue(log.getCostTime() != null ? log.getCostTime().toString() : "");
        }
    }




    /**
     * 页面数据封装类
     */
    private static class PageData {
        final int pageIndex;
        final List<SysOperLog> data;

        PageData(int pageIndex, List<SysOperLog> data) {
            this.pageIndex = pageIndex;
            this.data = data;
        }
    }
}

